# Auther：wujehy
# email:wujehy@qq.com
# @File : ProcUnit.py 
# @Time : 19-6-19 下午9:51
import json
import time
from queue import Queue

from .ToolUnit import ToolUnit
import paho.mqtt.client as mqtt

from .models import *
class ProcUnit(ToolUnit):
    __mqttClient = ''
    __sensorList = {}
    __msgQueue = {}
    __mqttHost = "iotgeek.top"
    __mqttPort = 1883
    userData = {}

    def __init__(self):
        # self.__CleanTime=self.StringtoData(self.TimeNow())
        # self.__CleanTime=threading.Thread(target=self.CleanData)
        # self.__CleanTime.start()
        self.__mqttClient = mqtt.Client()
        self.__mqttClient.connect(self.__mqttHost, self.__mqttPort, 60)

        self.__mqttClient.subscribe("wujehy", 1)
        # self.__CleanTime.start()
        self.__mqttClient.loop_start()
        return

    def addSensorList(self,panid ,obj,Type=False,Show=False):
        """
        将数据 存进 sensorlist
        # 如果 字典 没有 关键子 则创建 字典
        # 如果 存在 则 修改 更新

        :param panid: 上传 的panid
        :param obj:  数据对象
        :param Type:  类型 False 是 字典 格式 True 是 对象 格式
        :param Show  是否遍历 当前全部信息
        :return: True 是 成功  false 是 失败
        """
        status=False
        # 如果 字典 没有 关键子 则创建 字典
        # 如果 存在 则 修改 更新

        if panid in self.__sensorList:
            pass
        else:
            self.__sensorList[panid]={}

        if Type:
            self.__sensorList[panid]["%s:%s" % (obj.Appid, obj.SensorName)]=obj
            status=True
        else:
            sensorID=self.SensorNamebyAppidAndID(obj.Appid,obj.SensorName)
            sensorType="id_%s:%s" % (hex(obj.Appid),obj.SensorName)
            print(sensorID)
            self.__sensorList[panid][sensorID]={
                    "value": self.sensorValue(sensorType, obj.SensorValue),
                    "time": obj.lastdatetime,
                    "unit":self.sensorUnit(sensorType)
            }
            # self.__sensorList[panid]["%s:%s" % (hex(obj.Appid), obj.SensorName)] = obj.__dict__
            status=True
        #     因为这个方法是 只有字典才能直接 打印 所以限制一下

        # self.__sensorList[panid]=dicts
        if Show and status and Type is False:
            print(str(self.__sensorList))

        return status

    def addMsgQueue(self,panid,data):
        """
        输入 panid  和 data (base64 加密的)
        将数据解析进 消息队列
        :param panid: panid 字符 类型
        :param data: base64 字符串
        :return: 成功 或者失败
        """
        # 如果 这个节点的 已存在 则 消息队列就 不要了
        # 否则 创建他的队列
        print("start add")
        if panid in self.__msgQueue:
            pass
        else:
            self.__msgQueue[panid]=Queue(128)

        try:
            if self.__msgQueue[panid].full():
                # 强制处理
                self.Mqttsend(panid)
            self.__msgQueue[panid].put(data)
            self.Mqttsend(panid)
            return True
        except:
            return False

    def uploadData(self,panid,data,save=False):
        try:
            data = self.base64decode(data.encode())
            if data[0] is 0x01:
                # 传感器 信息 的处理
                print("sensor Data proc")
                # 数据 载入 第四个 数据是长度 没三个 一组
                len = data[3]
                for x in range(int(len / 3)):
                    # 数据是大端 格式 都将数据处理成 数字处理 后期 解析 或者 数据库处理
                    obj = SensorData()
                    obj.Appid = int.from_bytes(data[1:3], byteorder='big')  # 直接处理
                    obj.SensorName = data[4 + 3 * x]  # 后期 读取配置文件进行 修改
                    obj.SensorValue = int.from_bytes(data[4 + 3 * x + 1:4 + 3 * x + 3], byteorder='big')   # 获取具体数据
                    obj.lastdatetime = self.TimeNow(Type=True)  # 当前时间 例如 '2019-04-03T11:18:13'
                    # self.addSensorList(panid,obj,Show=True)
                    self.addSensor(panid,obj,Show=True)
                    print(obj.__dict__)
                #  默认不存进数据库
                if save:
                    if self.addDatatoDB(panid) is False:
                        return self.retTag("panid Error no save DataBase")

                self.procData(panid)
                return self.retTag("upload ok",True)
        except:
            return self.retTag("data Type error")

    def getDataRealtime(self,panid=None):
        if panid is None:
            if self.__sensorList.__len__() >= 1:

                return self.retTag(data=self.__sensorList, Type=True)
            else:
                return self.retTag("no data")
        else:
            if panid in self.__sensorList:
                return self.retTag(data=self.__sensorList[panid], Type=True)
            else:
                return self.retTag("no data")

    def getMsgQueueInPanid(self,panid,status=None):
        if panid not in self.__sensorList:
            return self.retTag("no panid in datalist")
        try:
            if panid in self.__msgQueue:
                data = self.__msgQueue[panid].get(timeout=2)
                data = {
                    "code": self.base64encode(data).decode(),
                    "len": self.__msgQueue[panid].qsize()
                }
                return self.retTag(data,Type=True)
            else:
                return self.retTag("msg is empty no panid")
        except:
            return self.retTag("panid mes is empty ")


        # pass



    def Mqttsend(self,panid):
        """
        发送 一个 动作 仅此而已
        :return:
        """
        print("test mqtt %s"%panid)
        self.__mqttClient.publish(topic="wujehy", payload=panid)  # ok 就是 桥阶层的 控制判断指令
        return True




    def getDataRealtime(self,panid=None):
        if panid is None:
            if self.__sensorList.__len__() >= 1:

                return self.retTag(data=self.__sensorList, Type=True)
            else:
                return self.retTag("no data")
        else:
            if panid in self.__sensorList:
                return self.retTag(data=self.__sensorList[panid], Type=True)
            else:
                return self.retTag("no data")

    def getMsgQueueInPanid(self,panid,status=None):
        if panid not in self.__sensorList:
            return self.retTag("no panid in datalist")
        try:
            if panid in self.__msgQueue:
                data = self.__msgQueue[panid].get(timeout=2)
                data = {
                    "code": self.base64encode(data).decode(),
                    "len": self.__msgQueue[panid].qsize()
                }
                return self.retTag(data,Type=True)
            else:
                return self.retTag("msg is empty no panid")
        except:
            return self.retTag("panid mes is empty ")


    def addSensorList(self,panid,obj,Type=False,Show=False):
        """
        将数据 存进 sensorlist
        # 如果 字典 没有 关键子 则创建 字典
        # 如果 存在 则 修改 更新

        :param panid: 上传 的panid
        :param obj:  数据对象
        :param Type:  类型 False 是 字典 格式 True 是 对象 格式
        :param Show  是否遍历 当前全部信息
        :return: True 是 成功  false 是 失败
        """
        status=False
        # 如果 字典 没有 关键子 则创建 字典
        # 如果 存在 则 修改 更新

        if panid in self.__sensorList:
            pass
        else:
            self.__sensorList[panid]={}

        if Type:
            self.__sensorList[panid]["%s:%s" % (obj.Appid, obj.SensorName)]=obj
            status=True
        else:
            sensorID=self.SensorNamebyAppidAndID(obj.Appid,obj.SensorName)
            sensorType="id_%s:%s" % (hex(obj.Appid),obj.SensorName)
            print(sensorID)
            self.__sensorList[panid][sensorID]={
                    "value": self.sensorValue(sensorType, obj.SensorValue),
                    "time": obj.lastdatetime,
                    "unit":self.sensorUnit(sensorType)
            }
            # self.__sensorList[panid]["%s:%s" % (hex(obj.Appid), obj.SensorName)] = obj.__dict__
            status=True
        #     因为这个方法是 只有字典才能直接 打印 所以限制一下

        # self.__sensorList[panid]=dicts
        if Show and status and Type is False:
            print(str(self.__sensorList))

        return status

    def getOpenid(self,username,password,ipAddr):
        """
        登录验证　成功　返回　openid
        失败返回　空
        :param username:
        :param password:
        :param ipAddr:
        :return:
        """

        user=GeeJoansUser.objects.get(username=username)
        strdata="%s%s%s" % (username, password, self.TimeNow(Type=True))
        openid = hashlib.md5(strdata.encode()).hexdigest()
        UserLog(Uid=user,ULip=ipAddr,ULnote='web',Uopenid=openid).save()
        return self.retTag(openid,Type=True)
        # try:
        #     m=Member.objects.get(username=username)
        #     if m.password is password:
        #         request.session['member_id'] = m.id
        #         return self.retTag(m.openid, Type=True)
        # except:
        #     pass
        #
        #
        # user=GeeJoansUser.objects.filter(username=username,password=password)
        # if user.exists():
        #     uid=user.get(username=username,password=password).Uid
        #     openid="%s$s%s"%(username,password,self.TimeNow(Type=True))
        #     ul=UserLog(ULip=ipAddr,Uid=uid,ULnote="web",Uopenid=openid)
        #     ul.save()
        #     m=Member(username=username,password=password,openid=openid)
        #     m.save()
        #     request.session['member_id'] = m.id
        #     return self.retTag(openid,Type=True)
        # else:
        #     return self.retTag("Permission denied")

    def resetPasswordByKey(self,username,key,newPassword):
        try:
            u=GeeJoansUser.objects.get(password=key,username=username)
            u.set_password(newPassword)
            return self.retTag("set new Password OK",Type=True)
        except:
            return self.retTag("Permission denied")

    def forgetPassword(self,username):
        try:
            u=GeeJoansUser.objects.get(username=username)

            key=u.password
            email=u.email
            # send_mail("GeeJoans 找回密码","url:http://127.0.0.1:8000/")
            return self.retTag("email:%s"%email,Type=True)
        except:
            pass

    def procData(self, panid):
        """
        这是一和处理现成
        当上传一个数据 后 就会启动一次
        实验是 当  panid 的温度 到达 36 的时候就 发送执行指令 开灯
        这种方式 是存在延时  因为 队列的清理需要时间 如果 信息不多的话 问题不大
        :param panid: 上传者的信息
        :return:
        """
        for x in  self.__sensorList[panid]:
            # 筛选出 温度的 标志
            # 一定是 4位数字的 16 进制的 panid
            obj=SensorData()
            obj.__dict__=self.__sensorList[panid][x]
            if x is "temp":
                if float(obj.value)>36:
                    # 开灯指令
                    # 适配 panid
                    code=b"\x02\x12\x34\x02\x01\x01\r\n"
                    low=int(panid[0:2],16)
                    high=int(panid[2:4],16)
                    code=self.byteinData(code,1,low)
                    code=self.byteinData(code,2,high)
                    self.addMsgQueue(panid=panid,data=code)
                    print("open led",code)

    def addDatatoDB(self, panid):
        """
        将传感器 信息传输到 数据库
            用到 工具 将 传感器的 配置信息表 解析成 文字
            字典格式
        :param obj: 传感器数据对象
        :param panid: panid
        :return: True or False
        """
        try:
            devise=Devices.objects.filter(Dregister=panid)
            # projectData=ProjectData(
            if devise.exists():
                data = self.__sensorList[panid]
                # datas=json.dumps(data)
                projectData = ProjectData(Did=devise[0], PDtype="ender")
                projectData.PDdata= json.dumps(data)
                projectData.save()
                return True

            else:
                return False
        except:
            return False

    def getUserProjectDeviceByOpenid(self, openid):
        try:
            uid=UserLog.objects.get(Uopenid=openid).Uid
            pis=ProjectInformation.objects.filter(Uid=uid)
            ds=None
            for x in pis:
                if ds is None:
                    ds=Devices.objects.filter(Pid=x.Pid)
                else:
                    ds=ds.union(Devices.objects.filter(Pid=x.Pid))
            # pds=Devices.objects.filter(Pid=pis)
            ret = {}
            return  self.retTag(list(ds.extra(select={"panid":"Dregister"}).values("panid","Dtype","Ddatetime")),Type=True)

        except:
            pass

    def getProjectByopenid(self,openid):
        if UserLog.objects.filter(Uopenid=openid).exists():
            uid = UserLog.objects.get(Uopenid=openid).Uid
            pis = ProjectInformation.objects.filter(Uid=uid)
            if pis.exists():
                return self.retTag(list(pis.values("Ptitle","Pdescription","Pnote","Ptype","Pdatatime")))
            else:
                return self.retTag("don't have create project")
        else:
            return self.retTag("don't have this openid")

    def getDeviceByPid(self,Pid):
        ds=Devices.objects.filter(Pid=Pid)
        if ds.exists():
            return self.retTag(list(ds.values("Dregister","Dtype","Ddatetime",
                                              "Dstatus"),Type=True))
        else:
            return self.retTag("don't have Device")

    def getDataAllByDregister(self,Dregister):
        try:
            did=Devices.objects.get(Dregister=Dregister)
            pdatas=ProjectData.objects.filter(Did=did)
            if pdatas.exists():
                return self.retTag(list(pdatas.values()),Type=True)
            else:
                return self.retTag("the Devices data is None")
        except:
            return self.retTag("the Device is None")

    def getDataAtoBByDregister(self,Dregister,A:int,B:int):
        try:
            did=Devices.objects.get(Dregister=Dregister)
            pdatas=ProjectData.objects.filter(Did=did).extra(select={"datetime":"PDdatetime","data":"PDdata"})[A:B]
            if pdatas.exists():

                # datatest=pdatas.values("datetime","data")
                # return self.retTag(list(pdatas.values("datetime","data")),Type=True)
                return "success",list(pdatas.values()),self.TimeNow( )
            else:
                # return pdatas.values()
                return "error","emply",self.TimeNow()
                # return self.retTag("the Devices data is None")
        except:
            return self.retTag("the Device is None")

    def delProjectByPid(self,Pid,Uid):
        try:
            p=ProjectInformation.objects.filter(Pid=Pid,Uid=Uid)
            if p.exists():
                p.delete()
                return self.retTag("del Project OK",Type=True)
            else:
                return self.retTag("don't have Pid")
        except:
            return self.retTag("Permission denied")



    def delDeviceByDregister(self,Dregister):
        ds=Devices.objects.filter(Dregister=Dregister)
        if ds.exists():
            for x in ds:
                ProjectData.objects.filter(Did=x.Did).delete()
            ds.delete()
            return self.retTag("del Device and Data OK",Type=True)
        else:
            return self.retTag("don't have Devices")


    def addDevice(self,pid,Dtype,Dstatus=True,Ddata=None):
        if type(Ddata) is str:
            datas = Ddata + time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime())
        else:
            datas = str(Ddata) + time.strftime("%Y-%m-%dT%H:%M:%S", time.localtime())
        Dregister = hashlib.md5(datas.encode()).hexdigest()

        d=Devices(Pid=pid,Dtype=Dtype,Dstatus=Dstatus,Dregister=Dregister)
        d.save()
        return self.retTag(Dregister,Type=True)

    def addProject(self,Uid,Ptitle,Ptype=None,Pdescription=None,Pnote=None):
        try:
            pi=ProjectInformation(Uid=Uid,Ptype=Ptype,Ptitle=Ptitle,Pdescription=Pdescription,Pnote=Pnote)
            pi.save()
            return self.retTag("create Project OK",Type=True)
        except:
            return self.retTag("don't creata Project")

    def getPtype(self):
        pt=ProjectType.objects.all()
        return self.retTag(list(pt.values_list("PTid","PTname")))

    def doControl(self,panid,data):
        data = self.base64decode(data.encode())
        # ret=self.addMsgQueue(panid, data)
        ret = self.addMsgQueue(panid,data)
        # ret=True
        if ret:
            return self.retTag("control ok", Type=True)
        else:
            return self.retTag("error control")



    def __del__(self):
        self.__CleanTime.join()

    def addSensor(self, panid, obj, Show=False,Type=False):
        status=False
        if panid in self.__sensorList:
            pass
        else:
            self.__sensorList[panid]={}

        if Type:
            self.__sensorList[panid]["%s:%s" % (obj.Appid, obj.SensorName)]=obj
            status=True
        else:
            sensorID=self.SensorNamebyAppidAndID(obj.Appid,obj.SensorName)
            sensorType="id_%s:%s" % (hex(obj.Appid),obj.SensorName)
            print(sensorID)
            self.__sensorList[panid][sensorID]={
                    "value": self.sensorValue(sensorType, obj.SensorValue),
                    "time": obj.lastdatetime,
                    "unit":self.sensorUnit(sensorType)
            }
            # self.__sensorList[panid]["%s:%s" % (hex(obj.Appid), obj.SensorName)] = obj.__dict__
            status=True
        #     因为这个方法是 只有字典才能直接 打印 所以限制一下

        # self.__sensorList[panid]=dicts
        if Show and status and Type is False:
            print(str(self.__sensorList))

        return status

    # def addMsgInQueue(self, panid, data):
    #     print("test add")
    #     print(data)
    #     print(panid)
    #     # if panid in self.__msgQueue:
    #     #     pass
    #     # else:
    #     #     self.__msgQueue[panid] = Queue(128)
    #
    #     return True

    def addMsgQueue(self,panid,data):
        """
        输入 panid  和 data (base64 加密的)
        将数据解析进 消息队列
        :param panid:
        :param data:
        :return:
        """
        # 如果 这个节点的 已存在 则 消息队列就 不要了
        # 否则 创建他的队列

        if panid in self.__msgQueue:
            pass
        else:
            self.__msgQueue[panid]=Queue(128)

        try:
            if self.__msgQueue[panid].full():
                # 强制处理
                self.Mqttsend(panid)
            self.__msgQueue[panid].put(data)
            self.Mqttsend(panid)
            return True
        except:
            return False
        # pass
class SensorData:
    """
    传感器信息的对象
    """
    panid = ''
    Appid = ''
    SensorName = ''
    SensorValue = ''
    SensorUnit = ''
    lastdatetime = ''


